You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Always validate the gateway session captured when an async delegation was dispatched.
Treat compression as logical continuation: resolve the ended parent to its live compression tip and deliver there.
Advance stale gateway routing with a compare-and-swap update that cannot overwrite /new or reopen SQLite session rows.
Keep every unknown, non-compression, ended-tip, or foreign-route case fail-closed.
Problem
The July 8 async-completion guard only validates the captured gateway_session_id when it differs from the current gateway route.
That leaves two compression cases broken:
If sessions.json still points at the ended compressed parent, pinned == current bypasses the ended-row guard and the late completion can continue against the stale parent.
If routing already advanced to the compression child, the guard sees the captured parent as ended and drops a valid completion instead of following the same logical conversation.
This is a gateway follow-up to #55578, #57498, #57576, and #61993. It complements the classic-CLI ownership work in #64240; this PR does not touch CLI or TUI delivery.
Fix
For events carrying the captured parent session:
Read the captured session row even when it equals the current route.
If it is live, preserve the existing pinning behavior.
If it ended for compression, resolve the transitive compression tip, require that tip to be live, and prove the current route belongs to the same lineage.
CAS-advance only the gateway key→session mapping. If another turn moved the route first (for example /new), fail closed.
For every other end reason, lookup error, ended tip, or foreign route, drop injection; the durable delegation record remains available.
Blast radius
Producer scope:gateway_session_id is attached by the synthetic completion path from an event's parent_session_id; that field is produced by async delegations, not terminal/background-process completions.
Runtime scope: all messaging gateway adapters inherit the fix through GatewayRunner._handle_message; classic CLI and TUI paths are unchanged.
Persistence: no schema or transcript changes. The new SessionStore method only CAS-updates the existing routing index and deliberately does not call end_session or reopen_session.
Concurrency:/new wins the race. A stale snapshot cannot overwrite a newer route, and an already-advanced compression route is idempotent.
Cost: one async session-row lookup for pinned completions; compression-only cases add lineage/tip reads. AsyncSessionDB and AsyncSessionStore keep this work off the event loop.
Rollback: revert this single commit; no migration or state repair is required.
The sole failure is the checkout's existing Feishu SDK capability check (lark_oapi.ws.Client lacks extra_ua_tags in the local venv). The identical failure reproduces on a clean origin/main worktree: 209 passed, 1 failed.
Thanks for tracing the compression continuation case. The premise is verified on current main: SessionStore.get_or_create_session() heals a stale route to the compression tip (gateway/session.py:1870-1923), but the subsequent completion pinning guard drops the compression-ended captured parent rather than following that tip (gateway/run.py:10982-11007).
The proposed resolver uses the existing lineage-aware SessionDB.get_compression_tip() contract (hermes_state.py:3236-3297) and separates the safe compression mapping update from switch_session(), whose documented behavior ends the current row and can reopen the target (gateway/session.py:2322-2379). The changed tests cover live, unknown, non-compression-ended, stale-parent, already-advanced, intermediate-lineage, ended-tip, lookup-failure, and /new-CAS-loss cases.
No substantive correctness issue was found in the inspected diff. This is an automated hermes-sweeper review.
Merged via #69312 (commit b120121). Your commit was cherry-picked with your authorship preserved in git log — you're the base fix for the whole delegation-across-compression cluster (first submitter by 2 days). The salvage adds the #65838-inspired ack-semantics layer on top. Thanks!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/newor reopen SQLite session rows.Problem
The July 8 async-completion guard only validates the captured
gateway_session_idwhen it differs from the current gateway route.That leaves two compression cases broken:
sessions.jsonstill points at the ended compressed parent,pinned == currentbypasses the ended-row guard and the late completion can continue against the stale parent.This is a gateway follow-up to #55578, #57498, #57576, and #61993. It complements the classic-CLI ownership work in #64240; this PR does not touch CLI or TUI delivery.
Fix
For events carrying the captured parent session:
compression, resolve the transitive compression tip, require that tip to be live, and prove the current route belongs to the same lineage./new), fail closed.Blast radius
gateway_session_idis attached by the synthetic completion path from an event'sparent_session_id; that field is produced by async delegations, not terminal/background-process completions.GatewayRunner._handle_message; classic CLI and TUI paths are unchanged.end_sessionorreopen_session./newwins the race. A stale snapshot cannot overwrite a newer route, and an already-advanced compression route is idempotent.AsyncSessionDBandAsyncSessionStorekeep this work off the event loop.Verification
Broader gateway/delegation run:
The sole failure is the checkout's existing Feishu SDK capability check (
lark_oapi.ws.Clientlacksextra_ua_tagsin the local venv). The identical failure reproduces on a cleanorigin/mainworktree: 209 passed, 1 failed.Tested on Linux with Python 3.11.